import { Entity } from "../../../../../Core/Module/Entity/Entity";
import { Awake1System, IAwake1 } from "../../../../../Core/Module/EventSystem/IAwakeSystem";
import { DestroySystem, IDestroy } from "../../../../../Core/Module/EventSystem/IDestroySystem";
import { Log } from "../../../../../Core/Module/Log/Log";
import { NetServices } from "../../../../../Core/Module/Network/NetServices";
import { TimeHelper } from "../../../../../Core/Module/Time/TimeHelper";
import { ETTask } from "../../../../../Support/Share/Lib/Async/ETTask";
import { Dictionary } from "../../../../../Support/Share/Lib/Dictionary";
import { ExpandOf, FriendOf, ImplementsInterface, ObjectSystemAttribute } from "../../../../../Decorator/Decorator";
import { IMessage, IRequest, IResponse } from "./IMessage";
import { ErrorCore } from "../../../../../Core/Module/Network/ErrorCore";
import { Exception } from "../../../../../Core/Object/Exception";

export class RpcInfo {

    public readonly Request: IRequest;
    public readonly Tcs: ETTask<IResponse>;

    constructor(request: IRequest) {
        this.Request = request;
        this.Tcs = ETTask.Create<IResponse>();
    }

}

@ImplementsInterface(IAwake1)
@ImplementsInterface(IDestroy)
export class Session extends Entity implements IAwake1<number>, IDestroy {

    private _ServiceId: number;
    public get ServiceId(): number {
        return this._ServiceId;
    }
    public set ServiceId(value: number) {
        this._ServiceId = value;
    }

    private static _RpcId: number = 0;
    public static get RpcId(): number {
        return this._RpcId;
    }
    public static set RpcId(value: number) {
        this._RpcId = value;
    }

    readonly requestCallbacks = new Dictionary<number, RpcInfo>();

    private _LastRecvTime: number;
    public get LastRecvTime(): number {
        return this._LastRecvTime;
    }
    public set LastRecvTime(value: number) {
        this._LastRecvTime = value;
    }

    private _LastSendTime: number;
    public get LastSendTime(): number {
        return this._LastSendTime;
    }
    public set LastSendTime(value: number) {
        this._LastSendTime = value;
    }

    private _Error: number;
    public get Error(): number {
        return this._Error;
    }
    public set Error(value: number) {
        this._Error = value;
    }

    private _RemoteAddress: string;
    public get RemoteAddress(): string {
        return this._RemoteAddress;
    }
    public set RemoteAddress(value: string) {
        this._RemoteAddress = value;
    }

}

/**
 * 使用 Expand 扩展方法
 * 
 * 必须作出以下申明，才能调用对象的扩展方法
 */
declare module "./Session" {

    interface Session {

        OnResponse(response: IResponse): void;

        Call(request: IRequest): Promise<IResponse>;

        Send(message: IMessage): void;

        SendTo(actorId: number, message: IMessage): void;
    }

}

@ExpandOf(Session)
export class SessionSystem {

    private static OnResponse(self: Session, response: IResponse): void {
        let action = self.requestCallbacks.TryGetValue(response.RpcId);

        // console.log(`Session OnResponse`);
        // console.log(response);
        // console.log(action);
        if (!action) {
            return;
        }
        self.requestCallbacks.Remove(response.RpcId);
        if (ErrorCore.IsRpcNeedThrowException(response.Error)) {
            action.Tcs.SetException(new Exception(`Rpc error, request: ${action.Request} response: ${response}`));
            return;
        }
        action.Tcs.SetResult(response);
    }

    private static async Call(self: Session, request: IRequest): Promise<IResponse> {
        let rpcId = ++Session.RpcId;
        let rpcInfo = new RpcInfo(request);
        self.requestCallbacks.Add(rpcId, rpcInfo);
        request.RpcId = rpcId;

        // console.log(`Session Call`);
        // console.log(request);

        self.Send(request);
        return await rpcInfo.Tcs;
    }

    private static Send(self: Session, message: IMessage): void {
        self.SendTo(0, message);
    }

    private static SendTo(self: Session, actorId: number, message: IMessage): void {
        self.LastSendTime = TimeHelper.ClientNow();
        NetServices.Instance.SendMessage(self.ServiceId, self.Id, actorId, message);
    }

}

@FriendOf(Session)
@ObjectSystemAttribute()
export class SessionAwakeSystem extends Awake1System<Session, number> {

    Awake(self: Session, serviceId: number): void {
        self.ServiceId = serviceId;
        let timeNow = TimeHelper.ClientNow();
        self.LastRecvTime = timeNow;
        self.LastSendTime = timeNow;

        self.requestCallbacks.Clear();

        Log.Info(`session create: zone: ${self.DomainZone()} id: ${self.Id} ${timeNow}`);
    }

}

@FriendOf(Session)
@ObjectSystemAttribute()
export class SessionDestroySystem extends DestroySystem<Session> {

    Destroy(self: Session): void {
        NetServices.Instance.RemoveChannel(self.ServiceId, self.Id, self.Error);

        Log.Info(`session dispose: ${self.RemoteAddress} id: ${self.Id} ErrorCode: ${self.Error}, please see ErrorCode.cs! ${TimeHelper.ClientNow()}`);

        self.requestCallbacks.Clear();
    }

}